package com.ruoyi.newRepair.service.lmpl;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.newRepair.domain.*;
import com.ruoyi.newRepair.mapper.EquipmentMapper;
import com.ruoyi.newRepair.mapper.RepairOrderMapper;
import com.ruoyi.newRepair.service.EquipmentAnalysisService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import smile.clustering.KMeans;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.WeekFields;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class EquipmentAnalysisServiceImpl implements EquipmentAnalysisService {
    private static final ObjectMapper mapper = new ObjectMapper();
    @Autowired
    private final EquipmentMapper equipmentMapper;
    @Autowired
    private final RepairOrderMapper repairOrderMapper;

    /**
     * 获取设备统计信息
     * <p>
     * 此方法从repairOrderMapper获取设备统计信息，并对这些信息进行处理和转换
     * 它计算了设备的使用频率、总完成时间、平均完成时间、长时间和短时间任务的统计
     * 还计算了任务持续时间的方差和标准差，提供了关于任务持续时间波动性的见解
     *
     * @return 返回一个映射，键是设备ID，值是包含各种统计信息的映射
     */
    public Map<String, Map<String, Object>> getEquipmentStats() {
        return repairOrderMapper.getTop10EquipmentStats().stream()
                .collect(Collectors.toMap(
                        EquipmentStatsDTO::getEquipmentId,
                        dto -> {
                            Map<String, Object> stats = new LinkedHashMap<>();
                            stats.put("frequency", dto.getFrequency());
                            stats.put("TotalCompletionTime", dto.getTotalCompletionTime());
                            stats.put("AverageCompletionTime", dto.getAverageCompletionTime());
                            stats.put("LongTime", dto.getLongTime());
                            stats.put("ShortTime", dto.getShortTime());

                            // 从逗号分隔字符串解析原始数据
                            List<Double> durations = parseDurations(dto.getDurations());
                            // 计算任务持续时间的方差
                            double variance = calculateVariance(durations);

                            stats.put("variance", variance);
                            // 标准差是方差的平方根
                            stats.put("StandardDeviation", Math.sqrt(variance));
                            return stats;
                        },
                        (oldVal, newVal) -> oldVal,
                        LinkedHashMap::new
                ));
    }


    /**
     * 将逗号分隔的字符串解析为持续时间的列表
     * 此方法主要用于将格式化的字符串转换为可操作的数字列表
     *
     * @param concatStr 逗号分隔的字符串，每个元素代表一个持续时间
     * @return 解析后的持续时间列表
     */
    private List<Double> parseDurations(String concatStr) {
        return Arrays.stream(concatStr.split(","))
                .map(Double::valueOf)
                .collect(Collectors.toList());
    }

    /**
     * 计算给定数据集的方差
     * 方差是衡量数据集中各个数值与平均值之间差异程度的一个数值
     * 此方法用于统计分析，以了解数据集的离散程度
     *
     * @param data 包含数据点的列表
     * @return 数据集的方差，如果数据集为空，则返回0.0
     */
    private double calculateVariance(List<Double> data) {
        if (data.isEmpty()) return 0.0;
        double mean = data.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
        return data.stream()
                .mapToDouble(d -> Math.pow(d - mean, 2))
                .sum() / data.size();
    }


    //帕累托图
    public List<FaultCategoryStats> getFaultCategoryStats() {
        return repairOrderMapper.selectFaultCategoryStats();
    }


    //设备类型统计故障
    public Map<String, Object> getFailureStatistics(String startDate, String endDate) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedStart = startDate != null ? LocalDate.parse(startDate).atStartOfDay().format(formatter) : null;
        String formattedEnd = endDate != null ? LocalDate.parse(endDate).atTime(LocalTime.MAX).format(formatter) : null;

        List<FailureStatisticVO> stats = repairOrderMapper.selectFailureStatistics(formattedStart, formattedEnd);
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("dimensions", Arrays.asList("设备类型", "故障次数"));

        List<Map<String, Object>> source = stats.stream()
                .map(vo -> {
                    Map<String, Object> item = new HashMap<>();
                    item.put("设备类型", vo.getEquipmentTypeName());
                    item.put("故障次数", vo.getFailureCount());
                    return item;
                })
                .collect(Collectors.toList());

        result.put("source", source);
        return result;
    }


    //聚类分析
    public String analyzeClusters() throws Exception {
        // 1. 数据获取
        List<RepairOrder> data = repairOrderMapper.selectFailureData().stream()
                .filter(order ->
                        StringUtils.isNotBlank(order.getFailurePhenomenon()) &&
                                StringUtils.isNotBlank(order.getFailureCause()))
                .limit(20)
                .collect(Collectors.toList());
        // 2. 特征编码（简化版One-Hot）
        Map<String, Integer> phenomenonMap = buildFeatureMap(data, RepairOrder::getFailurePhenomenon);
        Map<String, Integer> causeMap = buildFeatureMap(data, RepairOrder::getFailureCause);
        // 3. 构建特征矩阵
        double[][] features = data.stream()
                .map(order -> new double[]{
                        phenomenonMap.getOrDefault(order.getFailurePhenomenon(), 1),
                        causeMap.getOrDefault(order.getFailureCause(), 0)
                })
                .toArray(double[][]::new);
        // 4. K-Means聚类（假设k=3）
        KMeans kmeans = KMeans.fit(features, 3);
        double[][] centroids = kmeans.centroids; // 获取质心坐标

        // 手动计算每个样本的聚类标签
        int[] labels = new int[features.length];
        for (int i = 0; i < features.length; i++) {
            double minDist = Double.MAX_VALUE;
            for (int j = 0; j < centroids.length; j++) {
                double dist = euclideanDistance(features[i], centroids[j]);
                if (dist < minDist) {
                    minDist = dist;
                    labels[i] = j; // 记录最近的质心索引
                }
            }
        }
        // 5. 构建可视化数据
        List<Object[]> seriesData = new ArrayList<>();
        for (int i = 0; i < data.size(); i++) {
            RepairOrder order = data.get(i);
            seriesData.add(new Object[]{
                    order.getFailurePhenomenon(),
                    order.getFailureCause(),
                    labels[i]
            });
        }
        // 原代码片段修改
        // 6. 生成ECharts JSON（修复版本）
        List<ScatterSeries> seriesList = new ArrayList<>();
        seriesList.add(new ScatterSeries(20, "scatter", seriesData));

        Map<String, Object> result = new HashMap<>();
        result.put("series", seriesList);
// 确保ObjectMapper配置包含空值
        mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        return mapper.writeValueAsString(result);
    }


    // 计算欧氏距离
    private double euclideanDistance(double[] a, double[] b) {
        double sum = 0.0;
        for (int i = 0; i < a.length; i++) {
            sum += Math.pow(a[i] - b[i], 2);
        }
        return Math.sqrt(sum);
    }

    // 特征编码方法（泛型方法优化）
    private <T> Map<String, Integer> buildFeatureMap(List<RepairOrder> data,
                                                     Function<RepairOrder, String> extractor) {
        return data.stream()
                .map(extractor)
                .distinct()
                .collect(Collectors.toMap(
                        Function.identity(),
                        v -> 1,       // 简化编码值
                        (oldVal, newVal) -> oldVal // 合并策略
                ));
    }

    //控制图
    public Map<String, Object> generateControlChartData() {
        List<RepairOrder> orders = Optional.ofNullable(repairOrderMapper.selectAllDurations())
                .orElse(Collections.emptyList());

        // 校验 orders 是否为空
        if (orders == null || orders.isEmpty()) {

            return Collections.emptyMap();
        }


        // 修改后的数据转换段（关键修复点）
        List<Double> durations = orders.stream()
                .filter(Objects::nonNull) // 新增：过滤空订单对象
                .map(RepairOrder::getCompletionDuration)
                .filter(d -> d != null && d >= 0) // 合并过滤条件
                .collect(Collectors.toList());

        // 处理空数据情况
        if (durations.isEmpty()) {

            return Collections.emptyMap();
        }

        // 统计计算
        DoubleSummaryStatistics stats = durations.stream()
                .mapToDouble(Double::doubleValue)
                .summaryStatistics();

        double xBar = stats.getAverage();
        double s = calculateStdDev(durations, xBar);

        double ucl = xBar + 3 * s;
        double lcl = Math.max(xBar - 3 * s, 0); // 防止负值

        // 构建数据集
        Map<String, Object> result = new HashMap<>();
        result.put("xAxis", createAxis("样本序号"));
        result.put("yAxis", createAxis("完成时长(小时)"));
        result.put("series", buildSeriesData(durations, xBar, ucl, lcl));
        return result;
    }

    /**
     * 创建一个轴的属性映射
     * 该方法用于生成一个包含轴名称和类型的键值对映射，主要用于图表或其他需要轴信息的场景
     *
     * @param name 轴的名称，用于标识轴的标题或名称
     * @return 返回一个映射对象，包含轴的名称和类型信息
     */
    private Map<String, String> createAxis(String name) {
        // 初始化一个HashMap用于存储轴的相关属性
        Map<String, String> axis = new HashMap<>();

        // 将轴的名称放入映射中，键为"name"
        axis.put("name", name);

        // 将轴的类型放入映射中，键为"type"
        axis.put("type", "value");

        // 返回构建好的轴属性映射
        return axis;
    }


    private List<Map<String, Object>> buildSeriesData(List<Double> durations,
                                                      double xBar, double ucl, double lcl) {
        // 散点图数据
        List<List<Double>> scatterData = new ArrayList<>();
        for (int i = 0; i < durations.size(); i++) {
            scatterData.add(Arrays.asList((double) i, durations.get(i)));
        }

        // 控制线数据
        List<List<Double>> uclData = new ArrayList<>();
        List<List<Double>> lclData = new ArrayList<>();
        List<List<Double>> meanData = new ArrayList<>();
        for (int i = 0; i < durations.size(); i++) {
            uclData.add(Arrays.asList((double) i, ucl));
            lclData.add(Arrays.asList((double) i, lcl));
            meanData.add(Arrays.asList((double) i, xBar));
        }

        // 构建系列集合
        List<Map<String, Object>> series = new ArrayList<>();

        // 散点图系列
        Map<String, Object> scatter = new HashMap<>();
        scatter.put("type", "scatter");
        scatter.put("data", scatterData);
        scatter.put("symbolSize", 20);
        series.add(scatter);

        // UCL线
        series.add(createLineSeries(uclData, "UCL", "red", "dashed"));
        // LCL线
        series.add(createLineSeries(lclData, "LCL", "green", "dashed"));
        // 均值线
        series.add(createLineSeries(meanData, "均值", "blue", "solid"));

        return series;
    }

    /**
     * 创建一个线性系列的配置信息
     * 该方法用于生成一个线性图表中的系列配置，包括数据、名称、颜色和线条类型
     *
     * @param data     系列的数据，每个内部列表代表一个数据点，包含X和Y坐标
     * @param name     系列的名称，用于图表的图例
     * @param color    线条的颜色，用于渲染线条
     * @param lineType 线条的类型，例如实线、虚线等
     * @return 返回一个包含系列配置信息的Map对象
     */
    private Map<String, Object> createLineSeries(List<List<Double>> data, String name,
                                                 String color, String lineType) {
        // 初始化系列配置的Map对象
        Map<String, Object> series = new HashMap<>();
        // 初始化线条样式的Map对象
        Map<String, String> lineStyle = new HashMap<>();

        // 设置线条的类型
        lineStyle.put("type", lineType);
        // 设置线条的颜色
        lineStyle.put("color", color);

        // 设置系列的类型为"line"，表示线性图表
        series.put("type", "line");
        // 设置系列的数据
        series.put("data", data);
        // 设置系列的名称
        series.put("name", name);
        // 设置系列的线条样式
        series.put("lineStyle", lineStyle);
        // 返回包含系列配置信息的Map对象
        return series;
    }


    // 样本标准差计算（n-1）
    private double calculateStdDev(List<Double> data, double mean) {
        if (data.size() <= 1) return 0.0;

        double sumSq = data.stream()
                .mapToDouble(d -> Math.pow(d - mean, 2))
                .sum();
        return Math.sqrt(sumSq / (data.size() - 1));
    }

    // 设备类型统计故障次数柱状图跳转
    public Map<String, Object> getFailureDetails(String deviceType, String startDate, String endDate) {
        // 转换日期格式（复用原有逻辑）
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedStart = startDate != null ? LocalDate.parse(startDate).atStartOfDay().format(formatter) : null;
        String formattedEnd = endDate != null ? LocalDate.parse(endDate).atTime(LocalTime.MAX).format(formatter) : null;

        // 查询详细信息
        List<Map<String, Object>> details = repairOrderMapper.selectFailureDetails(deviceType, formattedStart, formattedEnd);

        // 构建返回结构
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("deviceType", deviceType);
        result.put("totalCount", details.size());
        result.put("details", details.stream()
                .map(detail -> {
                    Map<String, Object> item = new HashMap<>();
                    item.put("equipmentId", detail.get("equipmentId"));
                    item.put("occurTime", detail.get("occurTime"));
                    item.put("description", detail.get("description"));
                    return item;
                }).collect(Collectors.toList()));
        return result;
    }

    @Override
    public Map<String, Object> getBasicStatistics(String equipmentNo, LocalDate startDate, LocalDate endDate) throws Exception {
        // 1. 获取设备数据
        List<Equipment> equipmentList = equipmentMapper.getEquipmentData(equipmentNo,
                startDate != null ? startDate.toString() : null,
                endDate != null ? endDate.toString() : null);

        if (equipmentList.isEmpty()) {
            throw new Exception("未找到符合条件的设备数据");
        }

        // 2. 计算基本统计量
        Map<String, Object> statistics = new HashMap<>();

        // 2.1 设备数量统计
        statistics.put("equipmentCount", equipmentList.size());

        // 2.2 故障类型统计
        Map<String, Long> faultTypeStats = equipmentList.stream()
                .collect(Collectors.groupingBy(Equipment::getFaultType, Collectors.counting()));
        statistics.put("faultTypeStats", faultTypeStats);

        // 2.3 维修时长统计
        DoubleSummaryStatistics repairTimeStats = equipmentList.stream()
                .mapToDouble(Equipment::getRepairTime)
                .summaryStatistics();

        Map<String, Double> repairStats = new HashMap<>();
        repairStats.put("average", repairTimeStats.getAverage());
        repairStats.put("max", repairTimeStats.getMax());
        repairStats.put("min", repairTimeStats.getMin());
        repairStats.put("total", repairTimeStats.getSum());
        statistics.put("repairTimeStats", repairStats);

        // 2.4 故障频率统计（按月份）
        Map<String, Long> monthlyStats = equipmentList.stream()
                .collect(Collectors.groupingBy(
                        e -> e.getFaultDate().substring(0, 7),  // 按 YYYY-MM 分组
                        Collectors.counting()
                ));
        statistics.put("monthlyStats", monthlyStats);

        // 2.5 计算百分位数
        List<Double> repairTimes = equipmentList.stream()
                .map(Equipment::getRepairTime)
                .sorted()
                .collect(Collectors.toList());

        Map<String, Double> percentiles = new HashMap<>();
        percentiles.put("25th", calculatePercentile(repairTimes, 25));
        percentiles.put("50th", calculatePercentile(repairTimes, 50));
        percentiles.put("75th", calculatePercentile(repairTimes, 75));
        statistics.put("percentiles", percentiles);

        return statistics;
    }

    /**
     * @param
     * @return 总利用率
     */
    @Override
    public Map<String, Object> countUR(List<String> equipmentIds, String startDate, String endDate) {
        Date earliestReceiveTime = repairOrderMapper.selectEarliestReceiveTime();
        Date latestCompletionTime = repairOrderMapper.selectLatestCompletionTime();
        LocalDate defaultStart = earliestReceiveTime != null ? toLocalDate(earliestReceiveTime) : LocalDate.now();
        LocalDate defaultEnd = latestCompletionTime != null ? toLocalDate(latestCompletionTime) : LocalDate.now();
        LocalDate start = (startDate != null && !startDate.isEmpty()) ? LocalDate.parse(startDate) : defaultStart;
        LocalDate end = (endDate != null && !endDate.isEmpty()) ? LocalDate.parse(endDate) : defaultEnd;
        int latestYear = end.getYear();
        int latestMonth = end.getMonthValue();
        List<String> xAxis = new ArrayList<>();
        LocalDate yearStart = start;
        while (yearStart.getYear() < latestYear) {
            xAxis.add(String.valueOf(yearStart.getYear()));
            yearStart = yearStart.plusYears(1);
        }
        LocalDate monthStart = LocalDate.of(latestYear, 1, 1);
        while (monthStart.getYear() == latestYear && monthStart.getMonthValue() <= latestMonth) {
            String ym = monthStart.format(DateTimeFormatter.ofPattern("MMM", Locale.ENGLISH));
            ym = monthStart.getYear() + "-" + ym;
            xAxis.add(ym);
            monthStart = monthStart.plusMonths(1);
        }
        LocalDate weekStart = LocalDate.of(latestYear, latestMonth, 1);
        LocalDate weekEnd = end;
        while (!weekStart.isAfter(weekEnd)) {
            int week = weekStart.get(WeekFields.ISO.weekOfWeekBasedYear());
            String weekLabel = "W" + String.format("%02d", week);
            xAxis.add(weekLabel);
            weekStart = weekStart.plusWeeks(1);
        }
        double maxY = 0;
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("xAxis", xAxis);
        result.put("maxY", 0.0);
        List<Map<String, Object>> allSeries = new ArrayList<>();
        if (equipmentIds.isEmpty()) {
            equipmentIds.add("");
        }
        for (String equipmentId : equipmentIds) {
            List<RepairOrder> allData = repairOrderMapper.selectAllForUR(start.toString(), end.toString(), equipmentId);
            if (allData == null) allData = new ArrayList<>();
            List<RepairOrder> validData = allData.stream()
                    .filter(Objects::nonNull)
                    .filter(r -> r.getReceiveTime() != null)
                    .filter(r -> r.getCompletionTime() != null)
                    .filter(r -> r.getEquipmentId() != null)
                    .filter(r -> r.getCompletionDuration() != null)
                    .collect(Collectors.toList());
            int deviceCount = 498;
            if (equipmentId != null && !equipmentId.trim().isEmpty()) {
                List<EquipmentCountDTO> equipmentCounts = countEquipment();
                Optional<EquipmentCountDTO> match = equipmentCounts.stream()
                        .filter(e -> equipmentId.equals(e.getEquipmentId()))
                        .findFirst();
                if (match.isPresent()) {
                    deviceCount = match.get().getRecordCount() == null ? 498 : match.get().getRecordCount().intValue();
                }
            }
            List<Double> series = new ArrayList<>();
            // 年分组
            yearStart = start;
            while (yearStart.getYear() < latestYear) {
                int year = yearStart.getYear();
                LocalDate groupStart = LocalDate.of(year, 1, 1);
                LocalDate groupEnd = LocalDate.of(year, 12, 31);
                if (groupEnd.isAfter(end)) groupEnd = end;
                double ur = calcUR(validData, groupStart, groupEnd, deviceCount);
                series.add(ur);
                yearStart = yearStart.plusYears(1);
            }
            // 月分组
            monthStart = LocalDate.of(latestYear, 1, 1);
            while (monthStart.getYear() == latestYear && monthStart.getMonthValue() <= latestMonth) {
                int msYear = monthStart.getYear();
                int msMonth = monthStart.getMonthValue();
                LocalDate groupStart = LocalDate.of(msYear, msMonth, 1);
                LocalDate groupEnd = groupStart.withDayOfMonth(groupStart.lengthOfMonth());
                if (groupEnd.isAfter(end)) groupEnd = end;
                double ur = calcUR(validData, groupStart, groupEnd, deviceCount);
                series.add(ur);
                monthStart = monthStart.plusMonths(1);
            }
            // 周分组
            weekStart = LocalDate.of(latestYear, latestMonth, 1);
            weekEnd = end;
            while (!weekStart.isAfter(weekEnd)) {
                LocalDate groupStart = weekStart;
                LocalDate groupFinish = weekStart.plusDays(6);
                if (groupFinish.isAfter(end)) groupFinish = end;
                double ur = calcUR(validData, groupStart, groupFinish, deviceCount);
                series.add(ur);
                weekStart = weekStart.plusWeeks(1);
            }
            double localMax = series.stream().mapToDouble(Double::doubleValue).max().orElse(0);
            if (localMax > maxY) maxY = localMax;
            Map<String, Object> seriesMap = new HashMap<>();
            String displayName = "".equals(equipmentId) ? "整体" : equipmentId;
            seriesMap.put("name", displayName);
            seriesMap.put("data", series);
            allSeries.add(seriesMap);
        }
        result.put("maxY", maxY);
        result.put("series", allSeries);
        return result;
    }

    /**
     * @param
     * @return 总维修时长
     */
    @Override
    public Map<String, Object> countfix(List<String> equipmentIds, String startDate, String endDate) {
        // 获取时间范围
        Date earliestReceiveTime = repairOrderMapper.selectEarliestReceiveTime();
        Date latestCompletionTime = repairOrderMapper.selectLatestCompletionTime();
        // 直接用数据库最早和最晚时间
        LocalDate defaultStart = earliestReceiveTime != null ? toLocalDate(earliestReceiveTime) : LocalDate.now();
        LocalDate defaultEnd = latestCompletionTime != null ? toLocalDate(latestCompletionTime) : LocalDate.now();
        // 使用传入的时间参数或默认值
        LocalDate start = (startDate != null && !startDate.isEmpty()) ? LocalDate.parse(startDate) : defaultStart;
        LocalDate end = (endDate != null && !endDate.isEmpty()) ? LocalDate.parse(endDate) : defaultEnd;

        int latestYear = end.getYear();
        int latestMonth = end.getMonthValue();

        // 生成x轴时间点
        List<String> xAxis = new ArrayList<>();

        // 添加年份
        LocalDate yearStart = start;
        while (yearStart.getYear() < latestYear) {
            xAxis.add(String.valueOf(yearStart.getYear()));
            yearStart = yearStart.plusYears(1);
        }

        // 添加月份
        LocalDate monthStart = LocalDate.of(latestYear, 1, 1);
        while (monthStart.getYear() == latestYear && monthStart.getMonthValue() <= latestMonth) {
            String ym = monthStart.format(DateTimeFormatter.ofPattern("MMM", Locale.ENGLISH));
            ym = monthStart.getYear() + "-" + ym;
            xAxis.add(ym);
            monthStart = monthStart.plusMonths(1);
        }

        // 添加周数
        LocalDate weekStart = LocalDate.of(latestYear, latestMonth, 1);
        LocalDate weekEnd = end;
        while (!weekStart.isAfter(weekEnd)) {
            int week = weekStart.get(WeekFields.ISO.weekOfWeekBasedYear());
            String weekLabel = "W" + String.format("%02d", week);
            xAxis.add(weekLabel);
            weekStart = weekStart.plusWeeks(1);
        }

        // 初始化结果
        double maxY = 0;
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("xAxis", xAxis);
        result.put("maxY", 0.0);
        List<Map<String, Object>> allSeries = new ArrayList<>();

        // 如果equipmentIds为空，查询所有设备类型的总维修时长
        if (equipmentIds.isEmpty()) {
            equipmentIds.add("");
        }
        // 处理每个设备类型
        for (String equipmentId : equipmentIds) {
            List<RepairOrder> allData = repairOrderMapper.selectAllForUR(start.toString(), end.toString(), equipmentId);

            if (allData == null) allData = new ArrayList<>();

            List<RepairOrder> validData = allData.stream()
                    .filter(Objects::nonNull)
                    .filter(r -> r.getReceiveTime() != null)
                    .filter(r -> r.getCompletionTime() != null)
                    .filter(r -> r.getEquipmentId() != null)
                    .filter(r -> r.getCompletionDuration() != null)
                    .collect(Collectors.toList());

            List<Double> series = new ArrayList<>();

            // 年分组
            yearStart = start;
            while (yearStart.getYear() < latestYear) {
                int year = yearStart.getYear();
                LocalDate groupStart = LocalDate.of(year, 1, 1);
                LocalDate groupEnd = LocalDate.of(year, 12, 31);
                if (groupEnd.isAfter(end)) groupEnd = end;

                double totalDuration = validData.stream()
                        .filter(r -> {
                            LocalDate d = toLocalDate(r.getCompletionTime());
                            return d != null && d.getYear() == year;
                        })
                        .mapToDouble(r -> r.getCompletionDuration() / 60.0)
                        .sum();

                series.add(Math.round(totalDuration * 100.0) / 100.0);
                yearStart = yearStart.plusYears(1);
            }

            // 月分组
            monthStart = LocalDate.of(latestYear, 1, 1);
            while (monthStart.getYear() == latestYear && monthStart.getMonthValue() <= latestMonth) {
                int msYear = monthStart.getYear();
                int msMonth = monthStart.getMonthValue();
                LocalDate groupStart = LocalDate.of(msYear, msMonth, 1);
                LocalDate groupEnd = groupStart.withDayOfMonth(groupStart.lengthOfMonth());
                if (groupEnd.isAfter(end)) groupEnd = end;

                double totalDuration = validData.stream()
                        .filter(r -> {
                            LocalDate d = toLocalDate(r.getCompletionTime());
                            return d != null && d.getYear() == msYear && d.getMonthValue() == msMonth;
                        })
                        .mapToDouble(r -> r.getCompletionDuration() / 60.0)
                        .sum();

                series.add(Math.round(totalDuration * 100.0) / 100.0);
                monthStart = monthStart.plusMonths(1);
            }

            // 周分组
            weekStart = LocalDate.of(latestYear, latestMonth, 1);
            weekEnd = end;
            while (!weekStart.isAfter(weekEnd)) {
                LocalDate groupStart = weekStart;
                LocalDate groupFinish = weekStart.plusDays(6);
                if (groupFinish.isAfter(end)) groupFinish = end;

                final LocalDate ws = weekStart;
                final LocalDate we = groupFinish;
                double totalDuration = validData.stream()
                        .filter(r -> {
                            LocalDate d = toLocalDate(r.getCompletionTime());
                            return d != null && !d.isBefore(ws) && !d.isAfter(we);
                        })
                        .mapToDouble(r -> r.getCompletionDuration() / 60.0)
                        .sum();

                series.add(Math.round(totalDuration * 100.0) / 100.0);
                weekStart = weekStart.plusWeeks(1);
            }

            double localMax = series.stream().mapToDouble(Double::doubleValue).max().orElse(0);
            if (localMax > maxY) maxY = localMax;

            Map<String, Object> seriesMap = new HashMap<>();
            String displayName = "".equals(equipmentId) ? "整体" : equipmentId;
            seriesMap.put("name", displayName);
            seriesMap.put("data", series);
            allSeries.add(seriesMap);
        }

        // 设置最终结果
        result.put("maxY", maxY);
        result.put("series", allSeries);
        return result;
    }

    /**
     * @return 总故障周趋势
     */
    @Override
    public Map<String, Object> totalFailure() {
        // 获取所有故障数据
        final List<TotalFailure> allData = repairOrderMapper.totalFailure() != null ?
                repairOrderMapper.totalFailure() : new ArrayList<>();

        List<String> xAxis = new ArrayList<>();
        List<Double> series = new ArrayList<>();

        // 获取所有年份
        Set<String> years = allData.stream()
                .map(TotalFailure::getYear)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        // 获取最新年份
        String latestYear = years.stream()
                .max(String::compareTo)
                .orElse("2024");

        // 1. 年分组（输出除最新年份外的所有年份）
        years.stream()
                .filter(year -> year.compareTo(latestYear) < 0)
                .sorted()
                .forEach(year -> {
                    // 获取该年所有周的故障数总和
                    double totalFailure = allData.stream()
                            .filter(d -> year.equals(d.getYear()))
                            .mapToDouble(d -> d.getTotalFailure() != null ? d.getTotalFailure() : 0.0)
                            .sum();

                    // 获取该年的周数
                    long weekCount = allData.stream()
                            .filter(d -> year.equals(d.getYear()))
                            .map(TotalFailure::getWeek)
                            .distinct()
                            .count();

                    // 计算平均值
                    double avgFailure = weekCount > 0 ? totalFailure / weekCount : 0.0;

                    xAxis.add(year);
                    series.add(Math.round(avgFailure * 100.0) / 100.0);
                });

        // 2. 月分组（只输出最新年份的1-12月）
        for (int i = 1; i <= 12; i++) {
            String month = String.format("%02d", i);
            final String targetMonth = month;

            // 获取该月所有周的故障数总和
            double totalFailure = allData.stream()
                    .filter(d -> latestYear.equals(d.getYear()) && targetMonth.equals(d.getMonth()))
                    .mapToDouble(d -> d.getTotalFailure() != null ? d.getTotalFailure() : 0.0)
                    .sum();

            // 获取该月的周数
            long weekCount = allData.stream()
                    .filter(d -> latestYear.equals(d.getYear()) && targetMonth.equals(d.getMonth()))
                    .map(TotalFailure::getWeek)
                    .distinct()
                    .count();

            // 计算平均值
            double avgFailure = weekCount > 0 ? totalFailure / weekCount : 0.0;

            // 转换月份为英文缩写
            String monthStr = LocalDate.of(2024, i, 1)
                    .format(DateTimeFormatter.ofPattern("MMM", Locale.ENGLISH));
            xAxis.add(latestYear + "-" + monthStr);
            series.add(Math.round(avgFailure * 100.0) / 100.0);
        }

        // 3. 周分组（输出最新年份最新月份的所有周）
        String latestMonth = allData.stream()
                .filter(d -> latestYear.equals(d.getYear()))
                .map(TotalFailure::getMonth)
                .max(String::compareTo)
                .orElse("01");

        allData.stream()
                .filter(d -> latestYear.equals(d.getYear()) && latestMonth.equals(d.getMonth()))
                .sorted((a, b) -> a.getWeek().compareTo(b.getWeek()))
                .forEach(d -> {
                    String weekLabel = "W" + d.getWeek();
                    xAxis.add(weekLabel);
                    series.add(Math.round(d.getTotalFailure() * 100.0) / 100.0);
                });

        double maxY = series.stream()
                .mapToDouble(Double::doubleValue)
                .max()
                .orElse(0.0);

        Map<String, Object> result = new HashMap<>();
        result.put("maxY", maxY);
        result.put("xAxis", xAxis);
        result.put("series", series);
        return result;
    }

    /**
     * @return Map<String, Object> 上周设备故障分布图
     */
    @Override
    public LastWeekFailure getLastWeekFailureDetails() {
        List<LastWeekFailure> list = repairOrderMapper.getLastWeekFailureDetails();
        if (list == null) return new LastWeekFailure();
        for (LastWeekFailure item : list) {
            if (item.getEquipment_id_prefix() == null || item.getEquipment_id_prefix().trim().isEmpty()) {
                item.setEquipment_id_prefix("其它");
            }
        }
        List<String> equipmentIdPrefixList = new ArrayList<>();
        List<Double> repairCountList = new ArrayList<>();
        List<Double> repairPercentage = new ArrayList<>();
        for (LastWeekFailure item : list) {
            equipmentIdPrefixList.add(item.getEquipment_id_prefix());
            repairCountList.add(item.getRepair_count());
            repairPercentage.add(item.getRepair_percentage());
        }
        LastWeekFailure lastWeekFailure = new LastWeekFailure();
        lastWeekFailure.setEquipmentIdPrefixList(equipmentIdPrefixList);
        lastWeekFailure.setRepairCountList(repairCountList);
        lastWeekFailure.setRepairPercentage(repairPercentage);
        return lastWeekFailure;
    }

    // 计算总利用率
    private double calcUR(List<RepairOrder> data, LocalDate groupStart, LocalDate groupEnd, int deviceCount) {
        // 统计天数
        Optional<LocalDate> minReceive = data.stream()
                .map(r -> toLocalDate(r.getReceiveTime()))
                .filter(Objects::nonNull)
                .filter(d -> !d.isAfter(groupEnd) && !d.isBefore(groupStart))
                .min(LocalDate::compareTo);
        Optional<LocalDate> maxComplete = data.stream()
                .map(r -> toLocalDate(r.getCompletionTime()))
                .filter(Objects::nonNull)
                .filter(d -> !d.isAfter(groupEnd) && !d.isBefore(groupStart))
                .max(LocalDate::compareTo);
        if (!minReceive.isPresent() || !maxComplete.isPresent()) return 0.0;
        long days = java.time.temporal.ChronoUnit.DAYS.between(minReceive.get(), maxComplete.get()) + 1;
        if (days <= 0) return 0.0;

        // 维修时长（分钟转小时/60）
        double totalDuration = data.stream()
                .filter(r -> {
                    LocalDate receive = toLocalDate(r.getReceiveTime());
                    LocalDate complete = toLocalDate(r.getCompletionTime());
                    return receive != null && complete != null &&
                            !receive.isAfter(groupEnd) && !complete.isBefore(groupStart) &&
                            !complete.isAfter(groupEnd);
                })
                .mapToDouble(RepairOrder::getCompletionDuration)
                .sum();
        // 总利用率公式
        double denominator = deviceCount * 24 * days;
        if (denominator == 0) return 0.0;
        double ur = ((denominator) - (totalDuration / 60.0)) / denominator;
        // 返回百分比，保留两位小数
        return Math.round(ur * 100.0);
    }

    //    将Date对象转换为LocalDate对象
    private LocalDate toLocalDate(Date date) {
        if (date == null) return null;
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * @return MTTR
     */
    @Override
    public Map<String, Object> countMTTR(List<String> equipmentIds, String startDate, String endDate) {
        Date earliestReceiveTime = repairOrderMapper.selectEarliestReceiveTime();
        LocalDate defaultStart = earliestReceiveTime != null ? toLocalDate(earliestReceiveTime) : LocalDate.of(2024, 1, 1);
        Date latestCompletionTime = repairOrderMapper.selectLatestCompletionTime();
        LocalDate defaultEnd = latestCompletionTime != null ? toLocalDate(latestCompletionTime) : LocalDate.now();
        LocalDate start = (startDate != null && !startDate.isEmpty()) ? LocalDate.parse(startDate) : defaultStart;
        LocalDate end = (endDate != null && !endDate.isEmpty()) ? LocalDate.parse(endDate) : defaultEnd;
        int latestYear = end.getYear();
        int latestMonth = end.getMonthValue();
        // 统一xAxis生成逻辑
        List<String> xAxis = new ArrayList<>();
        LocalDate yearStart = start;
        while (yearStart.getYear() < latestYear) {
            xAxis.add(String.valueOf(yearStart.getYear()));
            yearStart = yearStart.plusYears(1);
        }
        LocalDate monthStart = LocalDate.of(latestYear, 1, 1);
        while (monthStart.getYear() == latestYear && monthStart.getMonthValue() <= latestMonth) {
            String ym = monthStart.format(DateTimeFormatter.ofPattern("MMM", Locale.ENGLISH));
            ym = monthStart.getYear() + "-" + ym;
            xAxis.add(ym);
            monthStart = monthStart.plusMonths(1);
        }
        LocalDate weekStart = LocalDate.of(latestYear, latestMonth, 1);
        LocalDate weekEnd = end;
        while (!weekStart.isAfter(weekEnd)) {
            int week = weekStart.get(WeekFields.ISO.weekOfWeekBasedYear());
            String weekLabel = "W" + String.format("%02d", week);
            xAxis.add(weekLabel);
            weekStart = weekStart.plusWeeks(1);
        }
        double maxY = 0;
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("xAxis", xAxis);
        result.put("maxY", 0.0);
        List<Map<String, Object>> allSeries = new ArrayList<>();
        if (equipmentIds.isEmpty()) {
            equipmentIds.add("");
        }
        for (String equipmentId : equipmentIds) {
            List<RepairOrder> allData = repairOrderMapper.selectAllForMTTR(start.toString(), end.toString(), equipmentId);
            if (allData == null) allData = new ArrayList<>();
            List<RepairOrder> validData = allData.stream()
                    .filter(Objects::nonNull)
                    .filter(r -> r.getReceiveTime() != null)
                    .filter(r -> r.getEquipmentId() != null)
                    .filter(r -> r.getCompletionDuration() != null)
                    .collect(Collectors.toList());
            String regex = equipmentId;
            List<Double> series = new ArrayList<>();
            // 年分组
            yearStart = start;
            while (yearStart.getYear() < latestYear) {
                int year = yearStart.getYear();
                List<RepairOrder> yearList = validData.stream()
                        .filter(r -> {
                            LocalDate d = toLocalDate(r.getCompletionTime());
                            return d != null && d.getYear() == year;
                        })
                        .collect(Collectors.toList());
                double mttr = calcMTTR(yearList, regex);
                series.add(mttr);
                yearStart = yearStart.plusYears(1);
            }
            // 月分组
            monthStart = LocalDate.of(latestYear, 1, 1);
            while (monthStart.getYear() == latestYear && monthStart.getMonthValue() <= latestMonth) {
                int msYear = monthStart.getYear();
                int msMonth = monthStart.getMonthValue();
                List<RepairOrder> monthList = validData.stream()
                        .filter(r -> {
                            LocalDate d = toLocalDate(r.getCompletionTime());
                            return d != null && d.getYear() == msYear && d.getMonthValue() == msMonth;
                        })
                        .collect(Collectors.toList());
                double mttr = calcMTTR(monthList, regex);
                series.add(mttr);
                monthStart = monthStart.plusMonths(1);
            }
            // 周分组
            weekStart = LocalDate.of(latestYear, latestMonth, 1);
            weekEnd = end;
            while (!weekStart.isAfter(weekEnd)) {
                final LocalDate ws = weekStart;
                int week = weekStart.get(WeekFields.ISO.weekOfWeekBasedYear());
                LocalDate weekFinish = weekStart.plusDays(6);
                List<RepairOrder> weekList = validData.stream()
                        .filter(r -> {
                            LocalDate d = toLocalDate(r.getCompletionTime());
                            return d != null && !d.isBefore(ws) && !d.isAfter(weekFinish);
                        })
                        .collect(Collectors.toList());
                double mttr = calcMTTR(weekList, regex);
                series.add(mttr);
                weekStart = weekStart.plusWeeks(1);
            }
            double localMax = series.stream().mapToDouble(Double::doubleValue).max().orElse(0);
            if (localMax > maxY) maxY = localMax;
            Map<String, Object> seriesMap = new HashMap<>();
            String displayName = "".equals(equipmentId) ? "整体" : equipmentId;
            seriesMap.put("name", displayName);
            seriesMap.put("data", series);
            allSeries.add(seriesMap);
        }
        result.put("maxY", maxY);
        result.put("series", allSeries);
        return result;
    }

    // 计算MTTR
    private double calcMTTR(List<RepairOrder> list, String regex) {
        if (list == null || list.isEmpty()) {
            return 0.0;
        }

        // 编译正则表达式
        Pattern pattern = Pattern.compile(regex);

        // 过滤符合条件的维修单（设备ID匹配正则表达式且有完成时长）
        List<RepairOrder> filteredList = list.stream()
                .filter(Objects::nonNull)
                .filter(r -> r.getEquipmentId() != null && pattern.matcher(r.getEquipmentId()).find())
                .filter(r -> r.getCompletionDuration() != null)
                .collect(Collectors.toList());

        if (filteredList.isEmpty()) {
            return 0.0;
        }

        // 计算维修时长总和
        double totalRepairTime = filteredList.stream()
                .mapToDouble(RepairOrder::getCompletionDuration)
                .sum();

        // 计算维修单数
        long repairCount = filteredList.size();

        // MTTR = 维修时长总和 / 维修单数
        double mttr = totalRepairTime / repairCount;

        // 保留两位小数
        return Math.round(mttr * 100.0) / 100.0;
    }

    /**
     * @return
     */
    @Override
    public List<EquipmentCountDTO> countEquipment() {
        return repairOrderMapper.countEquipment();
    }

    /**
     * @param
     * @return MTBF
     */
    @Override
    public Map<String, Object> countMTBF(List<String> equipmentIds, String startDate, String endDate) {
        // 查询所有数据的时间范围
        Date earliestReceiveTime = repairOrderMapper.selectEarliestReceiveTime();
        Date latestCompletionTime = repairOrderMapper.selectLatestCompletionTime();
        LocalDate defaultStart = earliestReceiveTime != null ? toLocalDate(earliestReceiveTime) : LocalDate.now();
        LocalDate defaultEnd = latestCompletionTime != null ? toLocalDate(latestCompletionTime) : LocalDate.now();
        LocalDate start = (startDate != null && !startDate.isEmpty()) ? LocalDate.parse(startDate) : defaultStart;
        LocalDate end = (endDate != null && !endDate.isEmpty()) ? LocalDate.parse(endDate) : defaultEnd;
        int latestYear = end.getYear();
        int latestMonth = end.getMonthValue();
        // 统一xAxis生成逻辑（以第一个设备为基准）
        List<String> xAxis = new ArrayList<>();
        LocalDate yearStart = start;
        while (yearStart.getYear() < latestYear) {
            int year = yearStart.getYear();
            xAxis.add(String.valueOf(year));
            yearStart = yearStart.plusYears(1);
        }
        LocalDate monthStart = LocalDate.of(latestYear, 1, 1);
        while (monthStart.getYear() == latestYear && monthStart.getMonthValue() <= latestMonth) {
            String ym = monthStart.format(DateTimeFormatter.ofPattern("MMM", Locale.ENGLISH));
            ym = monthStart.getYear() + "-" + ym;
            xAxis.add(ym);
            monthStart = monthStart.plusMonths(1);
        }
        LocalDate weekStart = LocalDate.of(latestYear, latestMonth, 1);
        LocalDate weekEnd = end;
        while (!weekStart.isAfter(weekEnd)) {
            int week = weekStart.get(WeekFields.ISO.weekOfWeekBasedYear());
            String weekLabel = "W" + String.format("%02d", week);
            xAxis.add(weekLabel);
            weekStart = weekStart.plusWeeks(1);
        }
        double maxY = 0;
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("xAxis", xAxis);
        result.put("maxY", 0.0);
        List<Map<String, Object>> allSeries = new ArrayList<>();
        System.out.println("查询1" + equipmentIds);
        if (equipmentIds.isEmpty()) {
            equipmentIds.add("");
        }
        for (String equipmentId : equipmentIds) {
            List<RepairOrder> allData = repairOrderMapper.selectAllForMTBF(start.toString(), end.toString(), equipmentId);
            System.out.println("输出allData：" + allData);
            if (allData == null) allData = new ArrayList<>();
            List<RepairOrder> validData = allData.stream()
                    .filter(Objects::nonNull)
                    .filter(r -> r.getReceiveTime() != null)
                    .filter(r -> r.getCompletionTime() != null)
                    .filter(r -> r.getEquipmentId() != null)
                    .filter(r -> r.getCompletionDuration() != null)
                    .collect(Collectors.toList());
            int deviceCount = 498;
            if (equipmentId != null && !equipmentId.trim().isEmpty()) {
                List<EquipmentCountDTO> equipmentCounts = countEquipment();
                Optional<EquipmentCountDTO> match = equipmentCounts.stream()
                        .filter(e -> equipmentId.equals(e.getEquipmentId()))
                        .findFirst();
                if (match.isPresent()) {
                    deviceCount = match.get().getRecordCount() == null ? 498 : match.get().getRecordCount().intValue();
                }
            }
            List<Double> series = new ArrayList<>();
            // 年分组
            yearStart = start;
            while (yearStart.getYear() < latestYear) {
                int year = yearStart.getYear();
                LocalDate groupStart = LocalDate.of(year, 1, 1);
                LocalDate groupEnd = LocalDate.of(year, 12, 31);
                if (groupEnd.isAfter(end)) groupEnd = end;
                double mtbf = calcMTBF(validData, groupStart, groupEnd, deviceCount);
                series.add(mtbf);
                yearStart = yearStart.plusYears(1);
            }
            // 月分组
            monthStart = LocalDate.of(latestYear, 1, 1);
            while (monthStart.getYear() == latestYear && monthStart.getMonthValue() <= latestMonth) {
                int msYear = monthStart.getYear();
                int msMonth = monthStart.getMonthValue();
                LocalDate groupStart = LocalDate.of(msYear, msMonth, 1);
                LocalDate groupEnd = groupStart.withDayOfMonth(groupStart.lengthOfMonth());
                if (groupEnd.isAfter(end)) groupEnd = end;
                double mtbf = calcMTBF(validData, groupStart, groupEnd, deviceCount);
                series.add(mtbf);
                monthStart = monthStart.plusMonths(1);
            }
            // 周分组
            weekStart = LocalDate.of(latestYear, latestMonth, 1);
            weekEnd = end;
            while (!weekStart.isAfter(weekEnd)) {
                LocalDate groupStart = weekStart;
                LocalDate groupFinish = weekStart.plusDays(6);
                if (groupFinish.isAfter(end)) groupFinish = end;
                double mtbf = calcMTBF(validData, groupStart, groupFinish, deviceCount);
                series.add(mtbf);
                weekStart = weekStart.plusWeeks(1);
            }
            double localMax = series.stream().mapToDouble(Double::doubleValue).max().orElse(0);
            if (localMax > maxY) maxY = localMax;
            Map<String, Object> seriesMap = new HashMap<>();
            String displayName = "".equals(equipmentId) ? "整体" : equipmentId;
            seriesMap.put("name", displayName);
            seriesMap.put("data", series);
            allSeries.add(seriesMap);
        }
        result.put("maxY", maxY);
        result.put("series", allSeries);
        return result;
    }

    // 计算MTBF
    private double calcMTBF(List<RepairOrder> data, LocalDate groupStart, LocalDate groupEnd, int deviceCount) {
        // 统计天数
        Optional<LocalDate> minReceive = data.stream()
                .map(r -> toLocalDate(r.getReceiveTime()))
                .filter(Objects::nonNull)
                .filter(d -> !d.isAfter(groupEnd) && !d.isBefore(groupStart))
                .min(LocalDate::compareTo);
        Optional<LocalDate> maxComplete = data.stream()
                .map(r -> toLocalDate(r.getCompletionTime()))
                .filter(Objects::nonNull)
                .filter(d -> !d.isAfter(groupEnd) && !d.isBefore(groupStart))
                .max(LocalDate::compareTo);
        if (!minReceive.isPresent() || !maxComplete.isPresent()) return 0.0;
        long days = java.time.temporal.ChronoUnit.DAYS.between(minReceive.get(), maxComplete.get()) + 1;
        if (days <= 0) return 0.0;

        // 维修时长（分钟转小时/60）
        double totalDuration = data.stream()
                .filter(r -> {
                    LocalDate receive = toLocalDate(r.getReceiveTime());
                    LocalDate complete = toLocalDate(r.getCompletionTime());
                    return receive != null && complete != null &&
                            !receive.isAfter(groupEnd) && !complete.isBefore(groupStart) &&
                            !complete.isAfter(groupEnd);
                })
                .mapToDouble(RepairOrder::getCompletionDuration)
                .sum();

        // 维修次数
        long count = data.stream()
                .filter(r -> {
                    LocalDate receive = toLocalDate(r.getReceiveTime());
                    LocalDate complete = toLocalDate(r.getCompletionTime());
                    return receive != null && complete != null &&
                            !receive.isAfter(groupEnd) && !complete.isBefore(groupStart) &&
                            !complete.isAfter(groupEnd);
                })
                .count();

        // MTBF公式
        double mtbf = ((deviceCount * 24 * days) - (totalDuration / 60.0)) / (count == 0 ? 1 : count);
        return Math.round(mtbf * 100.0) / 100.0;
    }

    /**
     * @param
     * @return
     */
    @Override
    public List<EquipmentType> getEquipmentId() {
        return repairOrderMapper.getEquipmentId();
    }

    /**
     * @param deviceType
     * @return具体设备维修单分布
     */
    @Override
    public Map<String, Object> getSpecificEquipment(String deviceType) {
        List<SpecificEquipment> list = repairOrderMapper.getSpecificEquipment(deviceType);
        if (list == null || list.isEmpty()) {
            return new HashMap<>();
        }
        String title = list.get(0).getTitle();
        List<String> failurePhenomenon = new ArrayList<>();
        List<Double> repairCount = new ArrayList<>();
        List<Double> repairPercentage = new ArrayList<>();
        for (SpecificEquipment item : list) {
            failurePhenomenon.add(item.getFailurePhenomenon());
            repairCount.add(item.getRepairCount());
            repairPercentage.add(item.getRepairPercentage());
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("title", title);
        result.put("failurePhenomenon", failurePhenomenon);
        result.put("repairCount", repairCount);
        result.put("repairPercentage", repairPercentage);
        return result;
    }

    /**
     * @return OEE设备选取
     */
    @Override
    public List<EquipmentType> getOEEEquipmentId() {
        return repairOrderMapper.getOEEEquipmentId();
    }

    /**
     * @return OEE统计
     */
    @Override
    public Map<String, Object> countOEE(List<String> machineNos, String processSiteNo, String startDate, String endDate) {
        String start = (startDate == null || startDate.isEmpty()) ? null : startDate;
        String end = (endDate == null || endDate.isEmpty()) ? null : endDate;
        Map<String, Object> result = new LinkedHashMap<>();
        List<String> allTypes = Arrays.asList("W", "D", "ALM", "RG", "FG", "X", "F");
        boolean isAll = (machineNos == null || machineNos.isEmpty());
        List<String> targetNos = isAll ? allTypes : machineNos;
        
        // yearWeek -> machineNo -> OEE
        Map<String, Map<String, Double>> weekMachineOEE = new TreeMap<>();
        Set<String> allYearWeeks = new TreeSet<>();
        Map<String, List<Double>> yearMachineOEEList = new TreeMap<>(); // 年 -> machineNo -> OEE列表
        
        // 当machineNos为空时，需要收集所有设备的OEE数据来计算平均值
        Map<String, List<Double>> weekAllOEE = new TreeMap<>(); // yearWeek -> 所有设备的OEE列表
        Map<String, List<Double>> yearAllOEE = new TreeMap<>(); // 年 -> 所有设备的OEE列表
        
        for (String machineNo : targetNos) {
            if (machineNo == null) continue;
            String siteNo = processSiteNo;
            List<String> actualOps = null, planOps = null, totalOps = null;
            if ("W".equals(machineNo)) {
                siteNo = "擦洗";
                actualOps = Arrays.asList("T2", "T3", "T8", "T16");
                planOps = Arrays.asList("T2", "T3", "T8", "T16", "T5");
                totalOps = Arrays.asList("T2", "T3", "T8", "T16");
            } else if ("D".equals(machineNo)) {
                siteNo = "甩干机";
                actualOps = Arrays.asList("T2", "T3", "T8", "T16");
                planOps = Arrays.asList("T2", "T3", "T8", "T16", "T5");
                totalOps = Arrays.asList("T2", "T3", "T8", "T16");
            } else if ("ALM".equals(machineNo)) {
                siteNo = "自动上下料机";
                actualOps = Arrays.asList("T2", "T4");
                planOps = Arrays.asList("T2", "T4", "T5");
                totalOps = Arrays.asList("T2", "T4");
            } else if ("RG".equals(machineNo)) {
                siteNo = "RG";
                actualOps = Arrays.asList("T2", "T3", "T4", "T8");
                planOps = Arrays.asList("T2", "T3", "T4", "T8", "T5");
                totalOps = Arrays.asList("T2", "T3", "T4", "T8");
            } else if ("FG".equals(machineNo)) {
                siteNo = "FG";
                actualOps = Arrays.asList("T2", "T3", "T4", "T8");
                planOps = Arrays.asList("T2", "T3", "T4", "T8", "T5");
                totalOps = Arrays.asList("T2", "T3", "T4", "T8");
            } else if ("X".equals(machineNo)) {
                siteNo = "洗盒线";
                actualOps = Arrays.asList("T2", "T16");
                planOps = Arrays.asList("T2", "T16", "T5");
                totalOps = Arrays.asList("T2", "T16");
            } else if ("F".equals(machineNo)) {
                siteNo = "自动分片机";
                actualOps = Arrays.asList("T2", "T11", "T12");
                planOps = Arrays.asList("T2", "T11", "T12", "T5");
                totalOps = Arrays.asList("T2", "T11", "T12");
            } else {
                actualOps = Arrays.asList("T2", "T3", "T8", "T16");
                planOps = Arrays.asList("T2", "T3", "T8", "T16", "T5");
                totalOps = Arrays.asList("T2", "T3", "T8", "T16");
            }
            // Q1/Q2数据，按yearWeek分组
            List<OEE> oeeList = repairOrderMapper.countOEEQ1(machineNo, start, end);
            if (oeeList == null) oeeList = new ArrayList<>();
            Map<String, Double[]> weekStat = new HashMap<>(); // yearWeek -> [actualRun, planRun, run, totalRun]
            for (OEE oee : oeeList) {
                if (oee == null || oee.getOperationValue() == null || oee.getDurationTime() == null || oee.getYearWeek() == null) continue;
                String op = oee.getOperationValue();
                int duration = oee.getDurationTime();
                String yearWeek = oee.getYearWeek();
                allYearWeeks.add(yearWeek);
                Double[] arr = weekStat.computeIfAbsent(yearWeek, k -> new Double[]{0.0, 0.0, 0.0, 0.0});
                if (actualOps.contains(op)) arr[0] += duration;
                if (planOps.contains(op)) arr[1] += duration;
                if (op.equals("T2")) arr[2] += duration;
                if (totalOps.contains(op)) arr[3] += duration;
            }
            // Q3数据，按yearWeek分组
            List<Production> prodList = repairOrderMapper.countOEEQ3(siteNo, null, null);
            if (prodList == null) prodList = new ArrayList<>();
            Map<String, Double[]> weekQ3 = new HashMap<>(); // sweek -> [fz, fm]
            for (Production prod : prodList) {
                if (prod == null || prod.getSWeek() == null) continue;
                String sweek = prod.getSWeek();
                Double[] arr = weekQ3.computeIfAbsent(sweek, k -> new Double[]{0.0, 0.0});
                if (prod.getFz() != null) arr[0] += prod.getFz();
                if (prod.getFm() != null) arr[1] += prod.getFm();
            }
            // 合并OEE到总表
            for (String yw : weekStat.keySet()) {
                Double[] stat = weekStat.get(yw);
                Double[] q3arr = weekQ3.getOrDefault(yw, new Double[]{0.0, 0.0});
                double q1 = stat[1] > 0 ? (stat[0] * 100.0 / stat[1]) : 0;
                double q2 = stat[3] > 0 ? (stat[2] * 100.0 / stat[3]) : 0;
                double q3 = q3arr[1] > 0 ? (q3arr[0] / q3arr[1]) : 1.0;
                double oee = q1 * q2 * q3 * 0.01;
                oee = Math.round(oee * 100.0) / 100.0;
                weekMachineOEE.computeIfAbsent(yw, k -> new HashMap<>()).put(machineNo, oee);
                
                // 当machineNos为空时，收集所有设备的OEE数据用于计算平均值
                if (isAll) {
                    weekAllOEE.computeIfAbsent(yw, k -> new ArrayList<>()).add(oee);
                    String year = yw.length() >= 4 ? yw.substring(0, 4) : yw;
                    yearAllOEE.computeIfAbsent(year, k -> new ArrayList<>()).add(oee);
                }
                
                // 年数据收集
                String year = yw.length() >= 4 ? yw.substring(0, 4) : yw;
                yearMachineOEEList.computeIfAbsent(year + "", k -> new ArrayList<>()).add(oee);
            }
        }
        
        // 只保留最近五个yearWeek
        List<String> xAxis = new ArrayList<>();
        // 先加所有出现过的年份
        Set<String> allYears = new TreeSet<>();
        for (String yw : allYearWeeks) {
            if (yw.length() >= 4) allYears.add(yw.substring(0, 4));
        }
        xAxis.addAll(allYears);
        // 再加最近五个yearWeek
        List<String> weekList = new ArrayList<>(allYearWeeks);
        int n = weekList.size();
        if (n > 5) weekList = weekList.subList(n - 5, n);
        for (String yw : weekList) {
            if (!xAxis.contains(yw)) xAxis.add(yw);
        }
        
        // series
        List<Map<String, Object>> series = new ArrayList<>();
        
        if (isAll) {
            // 当machineNos为空时，只返回一个"整体"的平均值
            Map<String, Object> serie = new LinkedHashMap<>();
            serie.put("name", "整体");
            List<Double> oeeList = new ArrayList<>();
            for (String x : xAxis) {
                if (x.length() == 4) { // 年数据
                    List<Double> oees = yearAllOEE.getOrDefault(x, new ArrayList<>());
                    double avg = 0.0;
                    if (!oees.isEmpty()) {
                        avg = oees.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
                        avg = Math.round(avg * 100.0) / 100.0;
                    }
                    oeeList.add(avg);
                } else { // 周数据
                    List<Double> oees = weekAllOEE.getOrDefault(x, new ArrayList<>());
                    double avg = 0.0;
                    if (!oees.isEmpty()) {
                        avg = oees.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
                        avg = Math.round(avg * 100.0) / 100.0;
                    }
                    oeeList.add(avg);
                }
            }
            serie.put("data", oeeList);
            series.add(serie);
        } else {
            // 当machineNos不为空时，返回每个设备的OEE数据
            for (String machineNo : targetNos) {
                Map<String, Object> serie = new LinkedHashMap<>();
                serie.put("name", machineNo);
                List<Double> oeeList = new ArrayList<>();
                for (String x : xAxis) {
                    if (x.length() == 4) { // 年数据
                        List<Double> oees = yearMachineOEEList.getOrDefault(x, new ArrayList<>());
                        double avg = 0.0;
                        if (!oees.isEmpty()) {
                            avg = oees.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
                            avg = Math.round(avg * 100.0) / 100.0;
                        }
                        oeeList.add(avg);
                    } else { // 周数据
                        Double val = weekMachineOEE.getOrDefault(x, Collections.emptyMap()).get(machineNo);
                        oeeList.add(val != null ? val : 0.0);
                    }
                }
                serie.put("data", oeeList);
                series.add(serie);
            }
        }
        
        result.put("xAxis", xAxis);
        result.put("series", series);
        return result;
    }

    @Override
    public Map<String, Object> getRepairNumberStatistics() {
        // 获取维修单数据
        List<RepairOrder> orders = repairOrderMapper.selectRepairNumberData();

        // 使用LinkedHashMap保持键顺序
        Map<String, Object> result = new LinkedHashMap<>();

        // 双重空值防御
        List<Double> durations = Optional.ofNullable(orders).orElse(Collections.emptyList()).stream()
                .filter(Objects::nonNull)  // 过滤订单对象空值
                .map(RepairOrder::getCompletionDuration)
                .filter(Objects::nonNull)  // 过滤duration空值
                .sorted()
                .collect(Collectors.toList());

        if (durations.isEmpty()) {
            // 返回N/A格式与前端一致
            result.put("25th", "N/A");
            result.put("50th", "N/A");
            result.put("75th", "N/A");
            result.put("standardDeviation", "N/A");
            result.put("average", "N/A");
            result.put("count", 0);
            result.put("max", "N/A");
            result.put("min", "N/A");
            return result;
        }

        // 计算统计指标
        DoubleSummaryStatistics stats = durations.stream()
                .mapToDouble(Double::doubleValue)
                .summaryStatistics();

        // 标准差计算优化
        double mean = stats.getAverage();
        double variance = durations.stream()
                .map(d -> Math.pow(d - mean, 2))
                .mapToDouble(Double::doubleValue)
                .average()
                .orElse(0.0);
        double stdDev = Math.sqrt(variance);

        // 分位数计算加强边界检查
        result.put("25th", calculatePercentile(durations, 0.25));
        result.put("50th", calculatePercentile(durations, 0.50));
        result.put("75th", calculatePercentile(durations, 0.75));
        result.put("standardDeviation", String.format("%.2f", stdDev));
        result.put("average", String.format("%.2f", mean));
        result.put("count", stats.getCount());
        result.put("max", String.format("%.2f", stats.getMax()));
        result.put("min", String.format("%.2f", stats.getMin()));

        return result;
    }

    // 改进后的分位数计算方法
    private double calculatePercentile(List<Double> sortedData, double percentile) {
        if (sortedData == null || sortedData.isEmpty()) return 0.0;

        int size = sortedData.size();
        double index = percentile * (size - 1);
        int lower = (int) Math.floor(index);
        int upper = (int) Math.ceil(index);

        if (lower == upper) return sortedData.get(lower);

        double weight = index - lower;
        return sortedData.get(lower) * (1 - weight) + sortedData.get(upper) * weight;
    }


    // 辅助方法：计算百分位数

    private double calculatePercentile(List<Double> values, int percentile) {
        if (values == null || values.isEmpty()) {
            return 0.0;
        }

        int index = (int) Math.ceil(percentile / 100.0 * values.size()) - 1;
        return values.get(Math.max(0, Math.min(index, values.size() - 1)));
    }

    /**
     * @param equipmentNo
     * @param startDate
     * @param endDate
     * @return 设备编号统计
     */
    @Override
    public Map<String, Object> getDetails(String equipmentNo, String startDate, String endDate) {
//        日期封装
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedStart = startDate != null ? LocalDate.parse(startDate).atStartOfDay().format(formatter) : null;
        String formattedEnd = endDate != null ? LocalDate.parse(endDate).atTime(LocalTime.MAX).format(formatter) : null;

//        数据筛选
        List<EquipmentNo> stats = repairOrderMapper.selectEquipmentData(equipmentNo, formattedStart, formattedEnd);
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("dimensions", Arrays.asList("设备编号", "故障次数"));

//        数据获取
        List<Map<String, Object>> source = stats.stream()
                .map(stat -> {
                    Map<String, Object> item = new HashMap<>();
                    item.put("设备编号", stat.getEquipmentNo());
                    item.put("故障次数", stat.getFailureCount());
                    return item;
                })
                .collect(Collectors.toList());

//        结果返回
        result.put("source", source);
        return result;
    }

    /**
     * @param equipmentNo //     * @param repairStatsDTO
     * @return 设备编号详情
     */
    @Override
    public List<RepairOrder> getEquipmentNoDetails(String equipmentNo, /*RepairStatsDTO repairStatsDTO, */String startDate, String endDate, String completedByName) {
        return repairOrderMapper.getEquipmentNoDetails(equipmentNo, startDate, endDate, completedByName);
    }

    @Override
    public Long getEquipmentNoDetailsActions(String equipmentNo, String startDate, String endDate, String completedByName) {
        return repairOrderMapper.getEquipmentNoDetailsActions(equipmentNo, startDate, endDate, completedByName);
    }


}